﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Xml;
using System.Timers;
using System.IO;
using LibBot;
using System.Reflection;

namespace Trivia
{
    public class Trivia : IPlugin
    {
        private Core Bot;
        private string SelfDir;

        private string _categorykey;
        private string _categoryname;
        private string _hints;
        private string _autocorrect;
        private List<string> _questions = new List<string>();
        private List<string> _answers = new List<string>();
        private int _questioncount;
        private string _error;
        
        private bool _inprogress;
        private UInt32 _channelid;
        private List<string> _votes = new List<string>();
        private Dictionary<string, int> _scores = new Dictionary<string, int>();

        private int _questionnumber;
        private string _question;
        private string _answer;
        private string _hint;

        private Timer tmrPause = new Timer(2000);
        private Timer tmrHint = new Timer(30000);

        #region Plugin Info
        public string Name
        {
            get { return "Trivia"; }
        }

        public string Version
        {
            get { return "v1.0"; }
        }

        public string Description
        {
            get { return "Custom trivia in channels."; }
        }

        public List<char> BotFlags
        {
            get { return new List<char> { 't' }; }
        }

        public List<string> Commands
        {
            get { return new List<string> { "category", "question", "trivia" }; }
        }
        #endregion

        public Trivia()
        {
        }

        public void Initialize(Core Host)
        {
            Bot = Host;
            Bot.onChannelMessage += new Core.ChannelMessageEventHandler(Bot_onChannelMessage);
            Bot.onCommand += new Core.CommandEventHandler(Bot_onCommand);
            tmrPause.Elapsed += new ElapsedEventHandler(tmrPause_Elapsed);
            tmrHint.Elapsed += new ElapsedEventHandler(tmrHint_Elapsed);
            SelfDir = AppDomain.CurrentDomain.BaseDirectory + "Plugins\\Trivia";
        }

        #region Bot Events
        void Bot_onCommand(object sender, CommandEventArgs e)
        {
            string[] parameters;
            UInt32 ChannelID = e.ChannelID;
            int parsedInt;
            _channelid = ChannelID;

            if (e.ChannelName == null)
            {
                ChannelID = Bot.ChannelID;
                _channelid = ChannelID;
            }

            switch (e.Command)
            {
                case "category":
                    if (_inprogress)
                    {
                        Bot.SendChannelMessage(ChannelID, "{0}The current trivia category is: {1}" + _categoryname + "{0}!");
                    }
                    else
                    {
                        Bot.SendChannelMessage(ChannelID, "{0}Trivia is not in progress!");
                    }
                    break;

                case "question":
                    if (_inprogress)
                    {
                        Bot.SendChannelMessage(ChannelID, "{0}Question #" + _questionnumber + " of " + _questioncount + ": [{1}" + _categorykey + "{0}] {1}" + _question);
                    }
                    else
                    {
                        Bot.SendChannelMessage(ChannelID, "{0}Trivia is not in progress!");
                    }
                    break;

                case "trivia":
                    if (e.GetParameters(2, out parameters))
                    {
                        switch (parameters[0])
                        {
                            default:
                                if (int.TryParse(parameters[1], out parsedInt))
                                {
                                    if (!_inprogress)
                                    {
                                        _inprogress = true;

                                        if (ExtractCategory(parameters[0], parsedInt))
                                        {
                                            _questionnumber = 0;
                                            _votes.Clear();
                                            _scores.Clear();

                                            GetQuestion();
                                            Bot.SendChannelMessage(ChannelID, "{0}The category is... {1}" + _categoryname + "{0}!");
                                            Bot.SendChannelMessage(ChannelID, "{0}Hints: {1}" + _hints + " {2}| {0}Auto-Correct: {1}" + _autocorrect);
                                            Bot.SendChannelMessage(ChannelID, "{0}Question #1 of " + parsedInt + ": [{1}" + _categorykey + "{0}] {1}" + _question);
                                            tmrHint.Start();

                                            _questioncount = parsedInt;
                                        }
                                        else
                                        {
                                            _inprogress = false;
                                            Bot.SendChannelMessage(ChannelID, _error);
                                        }
                                    }
                                }
                                else
                                {
                                    Bot.SendChannelMessage(ChannelID, "{0}Command does either {1}not exist {0}or using an {1}incorrect format{0}.");
                                }
                                break;
                        }
                    }
                    else if (e.GetParameters(1, out parameters))
                    {
                        switch (parameters[0])
                        {
                            case "end":
                                if (_inprogress)
                                {
                                    _inprogress = false;
                                    tmrHint.Stop();
                                    Bot.SendChannelMessage(ChannelID, "{0}Trivia has ended, thanks for playing!");
                                }
                                else
                                {
                                    Bot.SendChannelMessage(ChannelID, "{0}Trivia is not in progress!");
                                }
                                break;
                        }
                    }
                    break;
            }
        }

        void Bot_onChannelMessage(object sender, ChannelMessageEventArgs e)
        {
            UInt32 ChannelID = e.ChannelID;
            bool correctAnswer;

            if (ChannelID == _channelid && _inprogress)
            {
                if (bool.Parse(_autocorrect))
                {
                    if (Levenshtein.Compute(e.Message.ToLower(), _answer.ToLower()) <= 2)
                    {
                        correctAnswer = true;
                    }
                    else
                    {
                        correctAnswer = false;
                    }
                }
                else
                {
                    if (e.Message.ToLower() == _answer.ToLower())
                    {
                        correctAnswer = true;
                    }
                    else
                    {
                        correctAnswer = false;
                    }
                }

                if (correctAnswer)
                {
                    if (_scores.ContainsKey(e.Username))
                    {
                        _scores[e.Username]++;
                    }
                    else
                    {
                        _scores.Add(e.Username, 1);
                    }

                    Bot.SendChannelMessage(ChannelID, "{1}" + e.Username + " {0}got the correct answer of {1}" + _answer + "{0}!");

                    Bot.onChannelMessage -= Bot_onChannelMessage;
                    Pause();
                    while (tmrPause.Enabled) { };
                    Bot.onChannelMessage += Bot_onChannelMessage;

                    if (_questionnumber != _questioncount)
                    {
                        GetQuestion();
                        Bot.SendChannelMessage(ChannelID, "{0}Question #" + _questionnumber + " of " + _questioncount + ": [{1}" + _categorykey + "{0}] {1}" + _question);
                        tmrHint.Stop();
                        tmrHint.Start();
                    }
                    else
                    {
                        string temp = "";
                        _inprogress = false;
                        tmrHint.Stop();

                        List<KeyValuePair<string, int>> sortedScores = new List<KeyValuePair<string, int>>(_scores);
                        sortedScores.Sort((firstPair, nextPair) =>
                        {
                            return firstPair.Value.CompareTo(nextPair.Value);
                        });

                        if (sortedScores.Count == 1)
                        {
                            temp += "{1}" + sortedScores[0].Key + "{0}" + "(^w" + sortedScores[0].Value + "{0})" + " ";
                        }
                        else if (sortedScores.Count == 2)
                        {
                            temp += "{1}" + sortedScores[1].Key + "{0}" + "(^w" + sortedScores[1].Value + "{0})" + " ";
                            temp += "{1}" + sortedScores[0].Key + "{0}" + "(^w" + sortedScores[0].Value + "{0})" + " ";
                        }
                        else
                        {
                            for (int count = sortedScores.Count; count >= sortedScores.Count - 2; count--)
                            {
                                temp += "{1}" + sortedScores[count - 1].Key + "{0}" + "(^w" + sortedScores[count - 1].Value + "{0})" + " ";
                            }
                        }

                        Bot.SendChannelMessage(ChannelID, "{0}SCORE: " + temp);
                    }
                }
            }
        }
        #endregion

        #region Controllers
        private void GetQuestion()
        {
            Random randomizer = new Random();
            int random = randomizer.Next(0, _questions.Count);

            _question = _questions[random];
            _answer = _answers[random];
            _hint = "";

            if (_answer.Contains(" "))
            {
                string[] split = _answer.Split(' ');
                _hint = split[0];

                for (int count = 1; count <= split.Length - 1; count++)
                {
                    _hint += " " + split[count];
                }
            }
            else
            {
                for (int count = 1; count <= _answer.Length; count++) { _hint += '-'; }
            }

            _questions.RemoveAt(random);
            _answers.RemoveAt(random);
            _questionnumber++;
        }

        private bool ExtractCategory(string CategoryKey, int QuestionCount)
        {
            XmlDocument xmlDoc;
            XmlNodeList xmlCategories;
            Random randomizer = new Random();
            bool foundCategory = false;

            try
            {
                xmlDoc = new XmlDocument();
                xmlDoc.Load(SelfDir + "\\settings.xml");
                xmlCategories = xmlDoc.SelectNodes("/trivia/category");
            }
            catch
            {
                _error = "{0}Unable to load categories!";
                return false;
            }

            for (int count = 0; count <= xmlCategories.Count - 1; count++)
            {
                if (xmlCategories[count].Attributes.GetNamedItem("name").Value.ToLower() == CategoryKey.ToLower())
                {
                    _categorykey = xmlCategories[count].Attributes.GetNamedItem("name").Value;
                    _categoryname = xmlCategories[count].Attributes.GetNamedItem("fullname").Value;
                    _hints = xmlCategories[count].Attributes.GetNamedItem("hints").Value;
                    _autocorrect = xmlCategories[count].Attributes.GetNamedItem("autocorrect").Value;

                    if (QuestionCount <= xmlCategories[count].ChildNodes.Count)
                    {
                        foreach (XmlNode question in xmlCategories[count].ChildNodes)
                        {
                            _questions.Add(question.InnerText);
                            _answers.Add(question.Attributes[0].Value);
                        }
                    }
                    else
                    {
                        _error = "{0}Not enough questions to match the requested number!";
                        return false;   
                    }

                    foundCategory = true;
                    break;
                }
            }

            if (foundCategory)
            {
                return true;
            }
            else
            {
                _error = "{0}Category does not exist!";
                return false;
            }
        }

        void Pause()
        {
            tmrPause.Start();
        }
        #endregion

        #region Timers
        void tmrPause_Elapsed(object sender, ElapsedEventArgs e)
        {
            tmrPause.Stop();
        }

        void tmrHint_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (bool.Parse(_hints))
            {
                Random randomizer = new Random();
                int random;

                do
                {
                    random = randomizer.Next(0, _hint.Length - 1);
                } while (_hint[random] != '-');

                _hint = _hint.Insert(random, _answer[random].ToString());
                _hint = _hint.Remove(random + 1, 1);

                Bot.SendChannelMessage(_channelid, "{0}Hint: {1}" + _hint);
            }
        }
        #endregion
    }
}
